package edu.mercer.sse658.amazonbkp;

import java.io.Closeable;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.codehaus.jackson.JsonFactory;
import org.codehaus.jackson.map.ObjectMapper;

import com.amazonaws.AmazonClientException;
import com.amazonaws.AmazonServiceException;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.policy.Policy;
import com.amazonaws.auth.policy.Principal;
import com.amazonaws.auth.policy.Resource;
import com.amazonaws.auth.policy.Statement;
import com.amazonaws.auth.policy.Statement.Effect;
import com.amazonaws.auth.policy.actions.SQSActions;
import com.amazonaws.services.glacier.AmazonGlacierClient;
import com.amazonaws.services.glacier.model.DeleteArchiveRequest;
import com.amazonaws.services.glacier.model.InitiateJobRequest;
import com.amazonaws.services.glacier.model.InitiateJobResult;
import com.amazonaws.services.glacier.model.InvalidParameterValueException;
import com.amazonaws.services.glacier.model.JobParameters;
import com.amazonaws.services.glacier.model.MissingParameterValueException;
import com.amazonaws.services.glacier.model.ResourceNotFoundException;
import com.amazonaws.services.glacier.model.ServiceUnavailableException;
import com.amazonaws.services.sns.AmazonSNSClient;
import com.amazonaws.services.sns.model.CreateTopicRequest;
import com.amazonaws.services.sns.model.CreateTopicResult;
import com.amazonaws.services.sns.model.SubscribeRequest;
import com.amazonaws.services.sns.model.SubscribeResult;
import com.amazonaws.services.sqs.AmazonSQSClient;
import com.amazonaws.services.sqs.model.CreateQueueRequest;
import com.amazonaws.services.sqs.model.CreateQueueResult;
import com.amazonaws.services.sqs.model.GetQueueAttributesRequest;
import com.amazonaws.services.sqs.model.GetQueueAttributesResult;
import com.amazonaws.services.sqs.model.GetQueueUrlRequest;
import com.amazonaws.services.sqs.model.Message;
import com.amazonaws.services.sqs.model.ReceiveMessageRequest;
import com.amazonaws.services.sqs.model.SetQueueAttributesRequest;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.base.Throwables;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.base.Stopwatch;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

/**
 * Glacier amazon vault backup storage, retrieval.<p>
 *
 */

public class GlacierVault implements Closeable, ConfigConstants
{
	private Optional<AmazonGlacierClient> _glacierClient = Optional.absent();

	private Optional<AmazonSQSClient>     _queueClient   = Optional.absent();
    
	private Optional<AmazonSNSClient>     _topicClient   = Optional.absent();
    
	private Optional<ImmutableMap<String,String>>  
										  _topicData     = Optional.absent();

	private Optional<ImmutableMap<String,String>>  
										  _queueData     = Optional.absent();

	private GVBuilder _builder = new GVBuilder();

	private Optional<JobID> _activeTask = Optional.absent();
	
	/** Permit only one instance. */
	
	private static Integer _instances = 0;
	
	
	private GlacierVault()
	{ 
		super(); 
		
	}//constructor for null-object
	
	
	public GlacierVault( final GVBuilder builderIn )
	{
		super();

		init( builderIn );
		
	}//constructor
	
	
	/**
	 * Unusable GlacierVault instance for initialization purposes.<p>
	 * 
	 * Suitable as null object.<p>
	 * 
	 * @return  empty GlacierVault instance
	 */
	
	public static GlacierVault nought()
	{
		return new GlacierVault();
		
	}//method
	

	/**
	 * Initialization, validation of supplied builder instance and 
	 * 	credentials, properties.<p>
	 * 
	 * @param builderIn - GVBuilder
	 * 
	 * @return this
	 */
	
	private GlacierVault init( final GVBuilder builderIn )
	{
		checkArgument( _instances < 1, 
				"Only one GlacierVault instance supported" );
		
		checkNotNull( builderIn, 
				"null detected: Glacier Vault Builder" );

		_builder = builderIn;

		/* Load Amazon credentials. */
		
		_glacierClient = Optional.of( 
				new AmazonGlacierClient( _builder.awsCredentials()
												 .get() ) );
		
		checkArgument( _glacierClient.isPresent(), 
				"Failed initialization AmazonGlacierClient" );

		_topicClient = Optional.of( 
				new AmazonSNSClient( _builder.awsCredentials()
											 .get() ) );
		
		checkArgument( _topicClient.isPresent(), 
				"Failed initialization AmazonSNSClient, Topic" );
		

		_queueClient = Optional.of( 
				new AmazonSQSClient( _builder.awsCredentials()
											 .get() ) );
		
		checkArgument( _queueClient.isPresent(), 
				"Failed initialization AmazonSQSClient, Queue" );

		_glacierClient.get()
					  .setEndpoint( AWS_SETTINGS.REGION
							  					.endPoint("glacier") );
		
		_queueClient.get()
					.setEndpoint( AWS_SETTINGS.REGION
		  									  .endPoint("queue") );

		_topicClient.get()
					.setEndpoint( AWS_SETTINGS.REGION
											  .endPoint("topic") );

		_queueData = Optional.of( initQueue() );

		checkArgument( _queueData.get()
				 .size() > 0, 
				 "sqs queue failed to initialize" );	

		_topicData = Optional.of( initTopic() );
		
		checkArgument( _topicData.get()
								 .size() > 0, 
								 "sns topic failed to initialize" );	
				
		_instances++;
        
		return this;
		
	}//method

	
	private ImmutableMap<String,String> initQueue()
	{
        final CreateQueueRequest queCreateReq = new CreateQueueRequest()
        		.withQueueName( _builder.queue()
        								.get() );
      
        final CreateQueueResult queCreateResult = _queueClient.get()
        		.createQueue( queCreateReq );
        		
        final String queURLStr = queCreateResult.getQueueUrl();
        
        final GetQueueAttributesRequest queReq = 
        		new GetQueueAttributesRequest()
        				.withQueueUrl( queURLStr )
        				.withAttributeNames("QueueArn");
    
        final GetQueueAttributesResult queResult = _queueClient.get()
        		.getQueueAttributes(queReq);
        
        final String sqsQueueARN = queResult.getAttributes()
        									.get("QueueArn");
    
        final Policy sqsPolicy =
        		
        new Policy().withStatements(
                new Statement(Effect.Allow)
                .withPrincipals(Principal.AllUsers)
                .withActions(SQSActions.SendMessage)
                .withResources(new Resource(sqsQueueARN)));
        
        final ImmutableMap<String, String> queueAttributes = 
        		ImmutableMap.of( "Policy", sqsPolicy.toJson() );
        
        _queueClient.get()
        			.setQueueAttributes( new SetQueueAttributesRequest(
        												queURLStr, 
        												queueAttributes) );	
		
		return ImmutableMap.of( "queURLStr", queURLStr, 
								"sqsQueueARN", sqsQueueARN );

//		return ImmutableMap.of( "queURLStr", 
//				"https://sqs.us-east-1.amazonaws.com/990686917117/" + 
//				"musmlib_queue_001",
//				
//				"sqsQueueARN", 
//				"arn:aws:sqs:us-east-1:990686917117:musmlib_queue_001" );
		
	}//method

	
	private ImmutableMap<String,String> initTopic()
	{
        final CreateTopicRequest createTopicReq = new CreateTopicRequest()
        	.withName( _builder.topic()
        					   .get() );
        
        final CreateTopicResult createTopicResult = 
        		_topicClient.get()
        					.createTopic( createTopicReq );
        
        final String topicARN = createTopicResult.getTopicArn();

        final SubscribeRequest request2 = new SubscribeRequest()
        	.withTopicArn( topicARN )
        	.withEndpoint( _queueData.get()
        							 .get( "sqsQueueARN" ) )
        	.withProtocol( "sqs" );
        
        final SubscribeResult result2 = _topicClient.get()
        											.subscribe( request2 );
            
        final String snsSubscriptionARN = result2.getSubscriptionArn();
    
		return ImmutableMap.of( "topicARN", topicARN,
								"snsSubscriptionARN", snsSubscriptionARN );

//		return ImmutableMap.of( "snsSubscriptionARN", 
//				"arn:aws:sns:us-east-1:990686917117:backup_glacier:" +
//				"01625a95-30ae-4bd4-b4f0-99e91d9e5100",
//				"topicARN",
//				"arn:aws:sns:us-east-1:990686917117:backup_glacier" );
		
	}//method
	
	
	public static GVBuilder builder()
	{
		return new GVBuilder();
		
	}//method
	
	
	public GlacierVault withActiveTask( final JobID jobIDIn ) 
			throws IOException
	{
		_activeTask = Optional.of( jobIDIn );
		
		_activeTask.get().save( JOBS.ACTIVE_FILE_NAME.asString() );
		
		return this;
		
	}//method
	
	
	/**
	 * Vault contents listing returned from aws inventory retrieval job.<p>
	 * 
	 * <ol>
	 * <li>create job to list vault contents 
	 * 		
	 * <li>submit job, get job id, this as observer of JobID records it
	 *          
	 * <li>parse vault contents
	 * </ol> 		
	 * 
	 * @param Optional ExecutorService - if absent, will block for results
	 * 
	 * @param jobIn - JobID optional, contains job id for AWS remote job 
	 * already running or completed
	 * 
	 * @return List&#60;String&#62 - vault contents listing
	 * 
	 * @throws InterruptedException 
	 * @throws ExecutionException 
	 */
	
	public ImmutableList<String> contents( 
			
			final Optional<?> threadExIn, 
			
			JobID... jobIDIn )
			
			throws InterruptedException, 
				   ExecutionException,
				   IOException
	{
		LogCustom.obtainLog("contents", Level.INFO).entering(
				getClass().getCanonicalName(), "contents");
		
		final JobParameters jobParm = new JobParameters()
		
			.withType( AWS_SETTINGS.INVENTORY_JOB.asString() )
								   
			.withDescription( AWS_SETTINGS.INVENTORY_JOB_DESC.asString() )
										  
			.withSNSTopic( topicInfo().get( AWS_SETTINGS.TOPIC_ARN
														.asString() ) );

		checkNotNull( jobParm, "Null detected job parameters object" );

		final AmazonTask task = AmazonTask
				.builder()
				.glacierclient( glacierClient() )
				.jobParameters( jobParm )
				.vault( _builder.vault().get() )
				.queueClient( queueClient() )
				.queueURL( queueInfo().get( 
				    AWS_SETTINGS.QUEUE_URL.asString() ) )
				.build();
				
		/* option to resume, poll for completion of job already started */

		_activeTask = Optional.of( task.jobID() );
		
		if( _activeTask.get().unfinished( JOBS.ACTIVE_FILE_NAME
											  .asString() ) )
		{
			_activeTask.get().restore( JOBS.ACTIVE_FILE_NAME
										   .asString() );
			
			/* over write any method-provided job ID argument */
			
			if( jobIDIn.length > 0 )
			{
				LogCustom.obtainLog("contents", Level.INFO).warning(
						"discarding job id provided: " + jobIDIn[0] );

				LogCustom.obtainLog("contents", Level.INFO).warning(
						"instead resuming job id: " + _activeTask.get() );
				
			}//if
			
			JobID[] jobIDArg = { _activeTask.get() };
			
			jobIDIn = jobIDArg;	
			
		}//if
		
		
		if( jobIDIn.length > 0 )
		{			
			checkNotNull( jobIDIn[0], 
					"null detected job id argument" );
			
			checkArgument( jobIDIn[0].toString().length() > 0, 
					"zero length job id argument" );
			
			task.jobID( jobIDIn[0] );
			
			LogCustom.obtainLog("contents", Level.INFO).info(
					"job id provided: " + jobIDIn[0] );
			
		}//if

		checkNotNull( task, "Null detected AWS task as ListenableFuture" );

		/* create executor capable of running listenable futures */

		ListeningExecutorService threadEx;
		
		if( ! threadExIn.isPresent() )
		{
			threadEx = MoreExecutors
					   .listeningDecorator( Executors
							   				.newCachedThreadPool() );

		}
		else
		{
			checkArgument( threadExIn.get() instanceof 
					ListeningExecutorService, 
					"ListeningExecutorService expected, invalid contents " +
					"of Guava Optional" );
			
			threadEx = (ListeningExecutorService) threadExIn.get();
			
		}//if	
		
		/* 
		 * submit and get AmazonTask type of ListenableFuture, not a mere 
		 * Callable 
		 */

		threadEx.submit(task);

		/* add callback listener to fetch job output for when job finishes */

		task.addListener( new TaskOutput(task), threadEx );

		List<String> list = Collections.<String> emptyList();
		
		if( task.get().taskOutput().isPresent() ) list = task.get()
															 .taskOutput()
															 .get()
															 .lines(); 
		
		if( jobIDIn.length < 1 )
		{
			LogCustom.obtainLog("contents", Level.INFO)
				.info( "awaiting aws job id" );
			
			System.out.println( "awaiting aws job id" );			
			
			/* wait for a new aws job id */

			Stopwatch stopJobIDWatch = Stopwatch.createStarted();

			Stopwatch sleepJobIDwatch = Stopwatch.createStarted();
			
			while( task.jobID()
					   .toString()
					   .length() < 1 )
			{
				Thread.sleep( 10000L );

				if( sleepJobIDwatch.elapsed(TimeUnit.MINUTES) > 2 )
				{
					sleepJobIDwatch.reset();
					sleepJobIDwatch.start();
					
					String sleepMsg = "sleeping, awaiting aws job id" + 
							" creation: "  
							+ stopJobIDWatch.elapsed(TimeUnit.MINUTES)  
							+ " minutes";
					
					LogCustom.obtainLog("contents", Level.INFO)
						.info( sleepMsg );
					
					System.out.println( sleepMsg );
				}//if
			}//while
			
			stopJobIDWatch.stop();
			sleepJobIDwatch.stop();
			
		}//if
			
		_activeTask = Optional.of( task.get().jobID() );
		
		_activeTask.get().save( JOBS.ACTIVE_FILE_NAME
									.asString() );					

		LogCustom.obtainLog("contents", Level.INFO)
			.info("aws job id active: " + _activeTask);
		
		
		if( threadExIn.isPresent() )
		{
			LogCustom.obtainLog("contents", Level.INFO)
				.info("thread executor provided, not awaiting output");
			
			LogCustom.obtainLog("contents", Level.INFO).exiting(
				getClass().getCanonicalName(), "contents");
			
			return ImmutableList.copyOf( list );
			
		}//if
		
		Stopwatch stopWatch = Stopwatch.createStarted();

		Stopwatch sleepwatch = Stopwatch.createStarted();
		
		Boolean jobIDShown = false;
		
		String msg = "waiting for job to finish";
		
		LogCustom.obtainLog("contents", Level.INFO)
			.info(msg);
		
		System.out.println( msg );
		
		do
		{
			if( !jobIDShown && task.jobID()
								   .toString()
								   .length() > 0 )
			{
				jobIDShown = true;
				
				String jobIDmsg = "task running with job id: " 
						+ task.jobID();
				
				System.out.println( jobIDmsg );
				
				LogCustom.obtainLog("contents", Level.INFO)
					.info( jobIDmsg );
				
			}//if
			
			if( sleepwatch.elapsed(TimeUnit.MINUTES) > 9 )
			{
				sleepwatch.reset();
				sleepwatch.start();
				
				msg = "sleeping, waiting for job to finish: "  
						+ stopWatch.elapsed(TimeUnit.MINUTES)  
						+ " minutes";
				
				LogCustom.obtainLog("contents", Level.INFO)
					.info(msg);
				
				System.out.println(msg);
				
			}//if
			
			Thread.sleep( 5000L );

		}while( !task.isDone() );

		sleepwatch.stop();
		stopWatch.stop();

		LogCustom.obtainLog("contents", Level.INFO).info( 
				"elapsed minutes: " + stopWatch.elapsed(TimeUnit.MINUTES) );

		LogCustom.obtainLog("contents", Level.INFO).info( 
				"ran OK: " + task.get().ranOK() );
		
		if( task.get()
				.taskOutput()
				.isPresent() ) 
		{
			list = task.get()
					   .taskOutput()
					   .get()
					   .lines(); 

		}
		else
		{
		
			LogCustom.obtainLog("contents")
				.severe("no job output retrieved");
			
		}//if

		_activeTask.get().erase( JOBS.ACTIVE_FILE_NAME.asString() );
		
		return ImmutableList.copyOf( list );
		
	}//method
	

	/**
	 * Delete All convenience method. Retrieves vault contents, deletes.<p>
	 * 
	 * @return ImmutableList&#60;String&#62 - archives deleted
	 * 
	 * @throws InterruptedException
	 * 
	 * @throws ExecutionException
	 * 
	 * @throws IOException
	 * 
	 * @see #contents
	 */
	
	public ImmutableList<String> deleteAll() throws InterruptedException,
									  ExecutionException, 
									  IOException
	{
		return deleteAll( Collections.<String> emptyList() );
		
	}//method
	
	
	/**
	 * Delete every archive ID in vault.<p>
	 * 
	 * Retrieve inventory contents if empty Collection supplied.<p>
	 * 
	 * Contents() blocks, but will resume until inventory list is 
	 * 	ready: 3-5 hrs.
	 * 
	 * Makes deleteAll suitable for call, interruption, call again 
	 * 	via cron.
	 *
	 * @return ImmutableList&#60;String&#62 - archives deleted
	 * 
	 * @throws IOException 
	 * 
	 * @throws ExecutionException
	 *  
	 * @throws InterruptedException
	 * 
	 * @see #contents
	 */
	
	public ImmutableList<String> deleteAll( Collection<String> inventory ) 
			throws InterruptedException, 
				   ExecutionException, 
				   IOException
	{
		final List<String> deletedArchiveIDs = new ArrayList<>();  
		
		CustomLogger log = LogCustom.obtainLog( "deleteAll", Level.ALL );
		
		if( !inventory.iterator().hasNext() )
		{
			log.info( "inventory list not supplied, requesting contents" );
			
			inventory = contents( Optional.absent() );
			
		}//if
		
		Collection<String> archiveIDs = Collections2.filter(
				inventory, new ArchiveIDs() );
		
		final Iterator<String> arcvIDs = archiveIDs.iterator();
		
		if( ! arcvIDs.hasNext() )
		{ 
			log.warning("no archive ids found in inventory listing");
			
		}//if
		
		while( arcvIDs.hasNext() )
		{
			String arcvId = arcvIDs.next();
			
			if( ! delete(arcvId) )
			{
				log.severe("failed to delete: " + arcvId);
				
			}
			else
			{
				deletedArchiveIDs.add(arcvId);
				
			}//if
			
		}//while
		
		return ImmutableList.copyOf( deletedArchiveIDs );
		
	}//method
	
	
	/**
	 * Delete single archive from vault.<p>
	 * 
	 * Happens real-time, not an aws job submission, REST.<p>
	 * 
	 * @param archiveID - String aws glacier archive id
	 * 
	 * @return Boolean - T if successful
	 */
	
	public Boolean delete( final String archiveID )
	{
		checkNotNull( archiveID, "null detected archive id" );
		
		CustomLogger log = LogCustom.obtainLog("deletion", Level.ALL);
		
		Boolean success = false;
		
		try
		{		
			_glacierClient.get().deleteArchive(
					new DeleteArchiveRequest()
						.withVaultName( _builder.vault().get() )
						.withArchiveId( archiveID ) );
			
			success = true;
			
		}
		catch( final ResourceNotFoundException |
					 MissingParameterValueException |
					 ServiceUnavailableException |
					 InvalidParameterValueException err )
		{
			log.warning( "archive not found or unable to delete " + err );

        	for( StackTraceElement ele : err.getStackTrace() )
        		log.finest( ele.toString() );
			
		}
		catch( final AmazonServiceException err )
		{
			Throwables.propagate(err);
			
		}
		catch( final AmazonClientException err )
		{
			Throwables.propagate(err);
			
		}//try		
		
		return success;
		
	}//method
	
	
	public AmazonGlacierClient glacierClient()
	{ 
		return _glacierClient.get(); 
		
	}//method
	
	
	public ImmutableMap<String,String> queueInfo()
	{
		return _queueData.get();
		
	}//method

	
	public ImmutableMap<String,String> topicInfo()
	{
		return _topicData.get();
		
	}//method
	
	
	public AmazonSNSClient topicClient()
	{
		return _topicClient.get();
		
	}//method

	
	public AmazonSQSClient queueClient()
	{
		return _queueClient.get();
		
	}//method
	
	
	@Override
	public String toString()
	{
		return _builder.vault()
					   .get();
		
	}//method
	

	/**
	 * Clean-up, reset instance count.<p> 
	 * 
	 */
	
	@Override
	public void close() throws IOException 
	{
		_instances--;
		
	}//method

	
}//class


/**
 * Glacier Vault Builder.<p>
 * 
 * Create Glacier Vault object instance. Fluent api.
 *
 */

class GVBuilder implements ConfigConstants
{
	private Optional<AWSCredentials> _awsCred   = Optional.absent();
	
	
	private Optional<String> 		 _vaultStr  = Optional.absent();
	
	
	private Optional<String> 		 _regionStr = Optional.absent();
	
	
	private Optional<String> 		 _topicStr  = Optional.absent();
	
	
	private Optional<String> 		 _queueStr  = Optional.absent();	

	
	/** Long - minutes to wait for AWS glacier job to complete */
	
	private Optional<Long>			 _timeOut 	= Optional.absent();
	
	
	public GVBuilder()
	{
		super();
		
	}//constructor
	
	
	/**
	 *  Validate necessary builder properties are set for GlacierVault 
	 *  construction.<p>
	 * 
	 * @return GlacierVault - new instance using this builder
	 */
	
	public GlacierVault build()
	{
		checkArgument( awsCredentials().isPresent(), 
				"aws credentials not set" );

		checkArgument( queue().isPresent(), 
				"sqs queue not set" );

		checkArgument( topic().isPresent(), 
				"sns topic not set" );

		checkArgument( region().isPresent(), 
				"region not set" );

		checkArgument( vault().isPresent(), 
				"vault not set" );

		return new GlacierVault( this );
		
	}//method
	
	
	public GVBuilder withAWSCredentials( final AWSCredentials awsIn )
	{
		checkNotNull( awsIn, "null detected: AWSCredentials" );
		
		_awsCred = Optional.of( awsIn );
		
		return this;
		
	}//method
	
	
	public GVBuilder withVault( final String vaultIn )
	{
		checkNotNull( vaultIn, "null detected: vault name" );
		
		_vaultStr = Optional.of( vaultIn );
		
		return this;
		
	}//method

	
	public GVBuilder withRegion( final String regionIn )
	{
		checkNotNull( regionIn, "null detected: region" );
		
		_regionStr = Optional.of( regionIn );
		
		return this;
		
	}//method

	
	public GVBuilder withTopic( final String topicIn )
	{
		checkNotNull( topicIn, "null detected: sns topic" );
		
		_topicStr = Optional.of( topicIn );
		
		return this;
		
	}//method


	public GVBuilder withQueue( final String queueIn )
	{
		checkNotNull( queueIn, "null detected: sqs queue" );
		
		_queueStr = Optional.of( queueIn );
		
		return this;
		
	}//method

	
	/** @param timeOut - Long minutes await for AWS glacier job completion */
	
	public GVBuilder withTimeOut( final Long timeOut )
	{
		checkNotNull( timeOut, "null detected: time out" );
		
		_timeOut = Optional.of( timeOut );
		
		return this;
		
	}//method

	
	public Optional<AWSCredentials> awsCredentials(){ return _awsCred;   }
	
	
	public Optional<String> 		vault()         { return _vaultStr;  }
	
	
	public Optional<String> 		region()        { return _regionStr; }
	
	
	public Optional<String> 		topic()         { return _topicStr;  }
	
	
	public Optional<String> 		queue()         { return _queueStr;  }

	
	public Long	timeOut()
	{ 
		return _timeOut.isPresent() ? _timeOut.get() : JOBS.TIMEOUT.asLong();
				
	}//method
	
	
}//class


/**
 * Parse archive id's from inventory listing.<p>
 *
 */

class ArchiveIDs implements Predicate<String>, ConfigConstants
{
	@Fulfills("Predicate")
	@Override
	public boolean apply( final String input ) 
	{
		if(input == null) return false;
		
		final Matcher recMtchr = Pattern.compile( 
				AWS_SETTINGS.ARCHIVE_ID_LAYOUT.asString() ).matcher( input );

		if( !recMtchr.matches() ) return false;

		if( recMtchr.group(AWS_SETTINGS.ARCHIVE_REGEX_GROUP.asString())
				.length() != 138 )
		{ 
			LogCustom.obtainLog("archive_ids_predicate", Level.INFO)
				.warning("failed to find regex group for archive id for: " +
						input);
			
			return false;
			
		}//if
		
		return true;
		
	}//method

	
}//class